
Drawing a bezier curve requires 2 control points, which influence the curve of the line:
CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
CGContextSetLineWidth(context, 4)
CGContextMoveToPoint(context, startPoint.x, startPoint.y)
CGContextMoveToPoint(context, startPoint.x, startPoint.y)
CGContextAddCurveToPoint(context,
control1.x,
control1.y,
control2.x,
control2.y,
endPoint.x,
endPoint.y
)
CGContextStrokePath(context)
How does this work? To understand we can draw some lines & points to visualize it.
First we need some utility functions to make drawing a little easier. The first is to draw a point with a specified radius and color:
func drawPoint(context: CGContextRef, point: Point, color: UIColor, radius: CGFloat = 4, outlineColor: UIColor? = nil) {
let rect = CGRectMake(point.x - radius, point.y - radius, radius * 2, radius * 2)
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillEllipseInRect(context, rect)
if let outline = outlineColor {
CGContextSetStrokeColorWithColor(context, outline.CGColor)
CGContextSetLineWidth(context, 1)
CGContextStrokeEllipseInRect(context, rect)
}
}
We also need a way to interpolate a position on a line between two points:
func interpolatePosition(p1: Point, p2: Point, t: CGFloat) -> Point {
return Point(
x: (1 - t) * p1.x + t * p2.x,
y: (1 - t) * p1.y + t * p2.y
)
}
Given 4 points (start
, control1
, control2
, end
) you can connect the points with 3 lines like this:
CGContextMoveToPoint(context, startPoint.x, startPoint.y)
if control1 != nil {
CGContextAddLineToPoint(context, control1!.x, control1!.y)
}
if control2 != nil {
CGContextAddLineToPoint(context, control2!.x, control2!.y)
}
CGContextAddLineToPoint(context, endPoint.x, endPoint.y)
CGContextSetLineWidth(context, 1)
CGContextSetStrokeColorWithColor(context, UIColor.lightGrayColor().CGColor)
CGContextStrokePath(context)
Now we can create interpolated points on each line segment, t1
, t2
, t3
. As we change t
these points travel along their respective lines.
let t1 = interpolatePosition(startPoint, p2: control1!, t: t)
let t2 = interpolatePosition(control1!, p2: control2!, t: t)
let t3 = interpolatePosition(control2!, p2: endPoint, t: t)
drawPoint(context, point: t1, color: UIColor.greenColor(), radius: 4)
drawPoint(context, point: t2, color: UIColor.greenColor(), radius: 4)
drawPoint(context, point: t3, color: UIColor.greenColor(), radius: 4)
We can then use these points to connect 2 lines:
CGContextMoveToPoint(context, t1.x, t1.y)
CGContextAddLineToPoint(context, t2.x, t2.y)
CGContextAddLineToPoint(context, t3.x, t3.y)
CGContextSetStrokeColorWithColor(context, UIColor(white: 0.8, alpha: 1.0).CGColor)
CGContextStrokePath(context)
This gives us 2 lines, on which we can interpolate 2 more points along time t
:
let u1 = interpolatePosition(t1, p2: t2, t: t)
let u2 = interpolatePosition(t2, p2: t3, t: t)
drawPoint(context, point: u1, color: UIColor.purpleColor(), radius: 4)
drawPoint(context, point: u2, color: UIColor.purpleColor(), radius: 4)
And with these 2 points we can form another line:
CGContextMoveToPoint(context, u1.x, u1.y)
CGContextAddLineToPoint(context, u2.x, u2.y)
CGContextSetStrokeColorWithColor(context, UIColor(white: 0.7, alpha: 1.0).CGColor)
CGContextStrokePath(context)
Finally, we can interpolate a point along this line, also across time t
. This point, happens to trace the bézier curve exactly.
let v1 = interpolatePosition(u1, p2: u2, t: t)
drawPoint(context, point: v1, color: UIColor.orangeColor(), radius: 4)
This episode uses Swift 1.1, Xcode 6.