override func viewDidLoad()
{
super.viewDidLoad()
let straightLinePointData: [[CGFloat]] = [[100, 200], // start
[700, 200], // end
[400, 200], // control 1
[400, 200]] // control 2
let curvedLinePointData1: [[CGFloat]] = [[100, 300],
[700, 300],
[300, 400],
[500, 400]]
let curvedLinePointData2: [[CGFloat]] = [[100, 500],
[700, 500],
[300, 700],
[500, 300]]
draw(straightLinePointData)
draw(curvedLinePointData1)
draw(curvedLinePointData2)
}
func draw(linePointData: [[CGFloat]])
{
let path: CGMutablePathRef = CGPathCreateMutable()
let pathPointStart = CGPointMake(linePointData[0][0], linePointData[0][1])
let pathPointEnd = CGPointMake(linePointData[1][0], linePointData[1][1])
let pathPointControl1 = CGPointMake(linePointData[2][0], linePointData[2][1])
let pathPointControl2 = CGPointMake(linePointData[3][0], linePointData[3][1])
let m: UnsafePointer<CGAffineTransform> = nil
CGPathMoveToPoint(path, m, pathPointStart.x, pathPointStart.y)
CGPathAddCurveToPoint(path, m, pathPointControl1.x, pathPointControl1.y,
pathPointControl2.x, pathPointControl2.y,
pathPointEnd.x, pathPointEnd.y)
var visualPath = self.visualPath(path)
self.view.layer.addSublayer(visualPath)
func addNode(pathPercent: CGFloat)
{
let node = self.node()
let position = CGPointMake(positionForCubicBezierCurve(pathPercent,
p0: pathPointStart.x,
p1: pathPointControl1.x,
p2: pathPointControl2.x,
p3: pathPointEnd.x),
positionForCubicBezierCurve(pathPercent,
p0: pathPointStart.y,
p1: pathPointControl1.y,
p2: pathPointControl2.y,
p3: pathPointEnd.y))
// tangent
let derivative = CGPointMake(derivativeForCubicBezierCurve(pathPercent,
p0: pathPointStart.x,
p1: pathPointControl1.x,
p2: pathPointControl2.x,
p3: pathPointEnd.x),
derivativeForCubicBezierCurve(pathPercent,
p0: pathPointStart.y,
p1: pathPointControl1.y,
p2: pathPointControl2.y,
p3: pathPointEnd.y))
node.position = CGPointMake(position.x, position.y);
node.setAffineTransform(CGAffineTransformMakeRotation(atan2(derivative.y, derivative.x)))
node.allowsEdgeAntialiasing = true
self.view.layer.addSublayer(node)
}
addNode(0.02)
addNode(0.25)
addNode(0.50)
addNode(0.66)
addNode(0.75)
addNode(0.96)
addNode(1.0)
}
// See Wikipedia: Cubic Bezier curves
// Explicit Bezier formulas shown here, else Swift error: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions.
// t parameter is percentage of path
func positionForCubicBezierCurve(t:CGFloat, p0:CGFloat, p1:CGFloat, p2:CGFloat, p3:CGFloat) -> CGFloat
{
return ((1-t) * (1-t) * (1-t)) * p0 +
3 * ((1-t) * (1-t) ) * t * p1 +
3 * ((1-t) ) * t * t * p2 +
t * t * t * p3;
}
// tangent
func derivativeForCubicBezierCurve(t:CGFloat, p0:CGFloat, p1:CGFloat, p2:CGFloat, p3:CGFloat) -> CGFloat
{
return 3 * ((1-t) * (1-t)) * (p1 - p0) +
6 * ( 1-t ) * t * (p2 - p1) +
3 * t * t * (p3 - p2)
}
func visualPath(path: CGMutablePathRef) -> CAShapeLayer
{
let visualPath = CAShapeLayer()
visualPath.path = path
visualPath.fillColor = UIColor.clearColor().CGColor
visualPath.strokeColor = UIColor.darkGrayColor().CGColor
visualPath.lineWidth = 1.0
return visualPath
}
func node() -> CAGradientLayer
{
let node = CAGradientLayer()
node.frame = CGRectMake(0, 0, 20, 20)
node.borderColor = UIColor.darkGrayColor().CGColor
node.borderWidth = 0.50
// horizontal gradient
node.colors = [UIColor.init(white: 1, alpha: 0.5).CGColor, // top
UIColor.init(red: 1, green: 0, blue: 0, alpha: 0.5).CGColor] // bottom
node.locations = [NSNumber(float: 0.44),
NSNumber(float: 1)]
return node;
}