├── bezierCurve1.gif ├── BezierCurve ├── BezierCurve │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── ViewController.swift │ ├── SceneDelegate.swift │ └── BezierConfiguration.swift ├── BezierCurve.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcuserdata │ │ │ └── osamanaeem.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcuserdata │ │ └── osamanaeem.xcuserdatad │ │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ └── project.pbxproj ├── BezierCurveTests │ ├── Info.plist │ └── BezierCurveTests.swift └── BezierCurveUITests │ ├── Info.plist │ └── BezierCurveUITests.swift └── README.md /bezierCurve1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Onaeem26/CubicBezierCurveSwift/HEAD/bezierCurve1.gif -------------------------------------------------------------------------------- /BezierCurve/BezierCurve/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BezierCurve/BezierCurve.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurve.xcodeproj/project.xcworkspace/xcuserdata/osamanaeem.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Onaeem26/CubicBezierCurveSwift/HEAD/BezierCurve/BezierCurve.xcodeproj/project.xcworkspace/xcuserdata/osamanaeem.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /BezierCurve/BezierCurve.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CubicBezierCurveSwift 2 | Creating a smooth cubic bezier curve that pass through all the prescribed points using Swift and Math. 3 | 4 | In this project, we are calculating the control points to create a cubic bezier curve using Swift. 5 | More information about this project on my blog 6 | 7 | ![](/bezierCurve1.gif) 8 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurve.xcodeproj/xcuserdata/osamanaeem.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | BezierCurve.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurveTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurveUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurveTests/BezierCurveTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BezierCurveTests.swift 3 | // BezierCurveTests 4 | // 5 | // Created by Muhammad Osama Naeem on 4/18/20. 6 | // Copyright © 2020 Muhammad Osama Naeem. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import BezierCurve 11 | 12 | class BezierCurveTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurve/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BezierCurve 4 | // 5 | // Created by Muhammad Osama Naeem on 4/18/20. 6 | // Copyright © 2020 Muhammad Osama Naeem. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurveUITests/BezierCurveUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BezierCurveUITests.swift 3 | // BezierCurveUITests 4 | // 5 | // Created by Muhammad Osama Naeem on 4/18/20. 6 | // Copyright © 2020 Muhammad Osama Naeem. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class BezierCurveUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 20 | } 21 | 22 | override func tearDown() { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testExample() { 27 | // UI tests must launch the application that they test. 28 | let app = XCUIApplication() 29 | app.launch() 30 | 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | func testLaunchPerformance() { 36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 37 | // This measures how long it takes to launch your application. 38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 39 | XCUIApplication().launch() 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurve/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 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurve/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurve/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /BezierCurve/BezierCurve/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /BezierCurve/BezierCurve/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // BezierCurve 4 | // 5 | // Created by Muhammad Osama Naeem on 4/18/20. 6 | // Copyright © 2020 Muhammad Osama Naeem. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | let bezierPath = UIBezierPath() 14 | let data = [CGPoint(x: 60, y: 150), CGPoint(x: 140, y: 300),CGPoint(x: 230, y: 140), CGPoint(x: 310, y: 250), CGPoint(x: 390, y: 100), CGPoint(x: 490, y: 200), CGPoint(x: 580, y: 270),CGPoint(x: 650, y: 30) ] 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | let config = BezierConfiguration() 19 | let controlPoints = config.configureControlPoints(data: data) 20 | 21 | 22 | createPoints() 23 | 24 | for i in 0.. [BezierSegmentControlPoints] { 23 | 24 | 25 | let segments = data.count - 1 26 | 27 | 28 | if segments == 1 { 29 | 30 | // straight line calculation here 31 | let p0 = data[0] 32 | let p3 = data[1] 33 | 34 | return [BezierSegmentControlPoints(firstControlPoint: p0, secondControlPoint: p3)] 35 | }else if segments > 1 { 36 | 37 | //left hand side coefficients 38 | var ad = [CGFloat]() 39 | var d = [CGFloat]() 40 | var bd = [CGFloat]() 41 | 42 | 43 | var rhsArray = [CGPoint]() 44 | 45 | for i in 0.. [BezierSegmentControlPoints] { 91 | 92 | var controlPoints : [BezierSegmentControlPoints] = [] 93 | var ad = ad 94 | let bd = bd 95 | let d = d 96 | var rhsArray = rhsArray 97 | let segments = segments 98 | 99 | var solutionSet1 = [CGPoint?]() 100 | solutionSet1 = Array(repeating: nil, count: segments) 101 | 102 | //First segment 103 | ad[0] = ad[0] / d[0] 104 | rhsArray[0].x = rhsArray[0].x / d[0] 105 | rhsArray[0].y = rhsArray[0].y / d[0] 106 | 107 | //Middle Elements 108 | if segments > 2 { 109 | for i in 1...segments - 2 { 110 | let rhsValueX = rhsArray[i].x 111 | let prevRhsValueX = rhsArray[i - 1].x 112 | 113 | let rhsValueY = rhsArray[i].y 114 | let prevRhsValueY = rhsArray[i - 1].y 115 | 116 | ad[i] = ad[i] / (d[i] - bd[i]*ad[i-1]); 117 | 118 | let exp1x = (rhsValueX - (bd[i]*prevRhsValueX)) 119 | let exp1y = (rhsValueY - (bd[i]*prevRhsValueY)) 120 | let exp2 = (d[i] - bd[i]*ad[i-1]) 121 | 122 | rhsArray[i].x = exp1x / exp2 123 | rhsArray[i].y = exp1y / exp2 124 | } 125 | } 126 | 127 | //Last Element 128 | let lastElementIndex = segments - 1 129 | let exp1 = (rhsArray[lastElementIndex].x - bd[lastElementIndex] * rhsArray[lastElementIndex - 1].x) 130 | let exp1y = (rhsArray[lastElementIndex].y - bd[lastElementIndex] * rhsArray[lastElementIndex - 1].y) 131 | let exp2 = (d[lastElementIndex] - bd[lastElementIndex] * ad[lastElementIndex - 1]) 132 | rhsArray[lastElementIndex].x = exp1 / exp2 133 | rhsArray[lastElementIndex].y = exp1y / exp2 134 | 135 | solutionSet1[lastElementIndex] = rhsArray[lastElementIndex] 136 | 137 | for i in (0..