How to Get a List of Points from a Uibezierpath

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



Leave a reply



Submit