├── 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 | 
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..