Get points from a UIBezierPath
It should be possible to get points along a Bezier path as addQuadCurveToPoint is to add a quadratic Bezier segment into the path. So, the three control points of your first quadratic Bezier curve are (refer to the code piece in original post)
P(0) = origin
P(1) = (midpt1.x, midpt1.y+50)
P(2) = location
You can compute as many points on this quadratic Bezier curve as you want by varying the parameter t from 0 to 1 by any small increment value as
C(t) = (1-t)^2*P(0) + 2*t*(1-t)*P(1) + t^2 *P(2)
To get the Y value from a given X value, you will have to solve for the t value from the given X value from this quadratic polynomial of t:
X = (1-t)^2*X0 + 2*t*(1-t)*X1 + t^2 *X2
where X0, X1 and X2 are the X coordinates of P(0), P(1) and P(2), which means X0=origin.x, X1=midpt1.x and X2=location.x.
From this, we can obtain a quadratic equation
(X0-2*X1+X2)t^2 + 2(X1-X0)*t + (X0-X) = 0
You can solve for t using the quadratic formula. If your X0, X1 and X2 values happens to make the coefficient of t^2 term zero, you can solve for t directly as t = (X-X0)/(2*(X1-X0)).
Once you have the t value, you can easily evaluate the corresponding Y value.
Compute points from Bezier Path
I have found a solution written in objective-c. You can find the source code here.
I manage to use it by using a bridging header :
#ifndef bridging_header_h
#define bridging_header_h
#import "UIBezierPath+Length.h"
#endif /* bridge_header_h */
And you can use the two functions like this:
print("length=\(bezierPath.length())")
for i in 0...100 {
let percent:CGFloat = CGFloat(i) / 100.0
print("point at [\(percent)]=\(bezierPath.point(atPercentOfLength: percent))")
}
output:
length=143.316117804497
point at [0.0]=(3.0, 0.839999973773956)
point at [0.01]=(3.26246070861816, 1.29733419418335)
point at [0.02]=(3.97137236595154, 1.91627132892609)
point at [0.03]=(5.00902938842773, 2.69386911392212)
[...]
point at [0.99]=(3.27210903167725, 0.765813827514648)
point at [1.0]=(3.0, 0.839999973773956)
UIBezierPath circle get specific points
The equation for a circle is:
x = cx + r * cos(a)
y = cy + r * sin(a)
where r
is the radius, (cx, cy)
the origin, and a
the angle
you can draw a circle with
(UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
function by using a CGPoint
as centre and some value as the radius. You can give start angle and endangle as 0 and 360 for drawing the circle. Choose appropriate radius for the small circle and find the points using the equation mentioned at start and draw the circle
UIBezierPath - array of points from path?
Neither UIBezierPath
nor CGPath
have a method to obtain their points. This makes sense because the paths can be created with methods that are not simple points.
As @daveMac mentions in a comment there is a work-around:
void CGPathApply (
CGPathRef path,
void *info,
CGPathApplierFunction function
);
For each element in the specified path, Quartz calls the applier function, which can examine (but not modify) the element.
Get the start point of a UIBezierPath
You need to drop down to CGPath
and use CGPathApply
to walk the elements. You only want the first one, but you have to look at them all.
I'm assuming that your path is well-formed, and starts with a "move." That should always be true for a UIBezierPath
(I'm not aware of any way to make it not be true.)
You'll need some help from rob mayoff's CGPath.forEach
, which is quite tricky, but with that in place it's pretty straightforward:
// rob mayoff's CGPath.foreach
extension CGPath {
func forEach(@noescape body: @convention(block) (CGPathElement) -> Void) {
typealias Body = @convention(block) (CGPathElement) -> Void
func callback(info: UnsafeMutablePointer, element: UnsafePointer) {
let body = unsafeBitCast(info, Body.self)
body(element.memory)
}
let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer.self)
CGPathApply(self, unsafeBody, callback)
}
}
// Finds the first point in a path
extension UIBezierPath {
func firstPoint() -> CGPoint? {
var firstPoint: CGPoint? = nil
self.CGPath.forEach { element in
// Just want the first one, but we have to look at everything
guard firstPoint == nil else { return }
assert(element.type == .MoveToPoint, "Expected the first point to be a move")
firstPoint = element.points.memory
}
return firstPoint
}
}
In Swift 3, it's basically the same:
// rob mayoff's CGPath.foreach
extension CGPath {
func forEach( body: @convention(block) (CGPathElement) -> Void) {
typealias Body = @convention(block) (CGPathElement) -> Void
func callback(info: UnsafeMutableRawPointer?, element: UnsafePointer) {
let body = unsafeBitCast(info, to: Body.self)
body(element.pointee)
}
let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self)
self.apply(info: unsafeBody, function: callback)
}
}
// Finds the first point in a path
extension UIBezierPath {
func firstPoint() -> CGPoint? {
var firstPoint: CGPoint? = nil
self.cgPath.forEach { element in
// Just want the first one, but we have to look at everything
guard firstPoint == nil else { return }
assert(element.type == .moveToPoint, "Expected the first point to be a move")
firstPoint = element.points.pointee
}
return firstPoint
}
}
Related Topics
iOS 11 Disable Password Autofill Accessory View Option
Changing the Development Language in Xcode
iOS 13: Swift - 'Set Application Root View Controller Programmatically' Does Not Work
How to Combine React Native with Socket.Io
Swift 3 - How to Verify Class Type of Object
Nsurlsession: How to Increase Time Out for Url Requests
Uisearchbar Increases Navigation Bar Height in iOS 11
Xcode 9 Swift Language Version (Swift_Version)
Uigesturerecognizer and Uitableviewcell Issue
Customizing the More Menu on a Tab Bar
How to Add Background Image on iPhone Navigation Bar
Change Language in the App Programmatically in iOS
Property Not Working with Getter and Setter
Uploading Image with Afnetworking 2.0
How to Hide Keyboard in Swift on Pressing Return Key
Ios9 Does Not Load Insecure Resources from a Secure Page (Ssl/Https)