Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bézier Curves

Bézier Curves

Presented at try! Swift Tokyo 2018

Ben Scheirman

March 02, 2018
Tweet

More Decks by Ben Scheirman

Other Decks in Programming

Transcript

  1. Bézier Curves
    Ben Scheirman
    nsscreencast.com
    @subdigital

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. let path = UIBezierPath()
    path.move(to: startPoint)
    path.addCurve(to: endPoint, controlPoint1: c1, controlPoint2: c2)
    context.addPath(path.cgPath)
    context.strokePath()

    View Slide

  6. Control points?!

    View Slide

  7. Paul de Casteljau

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. class BezierView : UIView {
    var startPoint: CGPoint?
    var endPoint: CGPoint?
    var control1: CGPoint?
    var control2: CGPoint?
    ...

    View Slide

  21. func drawPoint(_ context: CGContext, p: CGPoint, color: UIColor, radius: CGFloat) {
    color.setFill()
    context.addArc(center: p,
    radius: radius,
    startAngle: 0,
    endAngle: .pi * 2,
    clockwise: true)
    context.fillPath()
    }

    View Slide

  22. Demo

    View Slide

  23. class BezierView : UIView {
    var startPoint: CGPoint?
    var endPoint: CGPoint?
    var control1: CGPoint?
    var control2: CGPoint?
    ...

    View Slide

  24. class BezierView : UIView {
    var points: [CGPoint]
    ...

    View Slide

  25. if points.count >= 2 {
    drawLinesBetween(context, points)
    }

    View Slide

  26. func drawLinesBetween(_ context: CGContext, _ points: [CGPoint]) {
    }

    View Slide

  27. func drawLinesBetween(_ context: CGContext, _ points: [CGPoint]) {
    points.first.flatMap { context.move(to: $0) }
    points.dropFirst().forEach { context.addLine(to: $0 ) }
    context.setLineWidth(3)
    context.setLineDash(phase: 0, lengths: [2])
    UIColor.lightGray.setStroke()
    context.strokePath()
    }

    View Slide

  28. func drawLinesBetween(_ context: CGContext, _ points: [CGPoint]) {
    points.first.flatMap { context.move(to: $0) }
    points.dropFirst().forEach { context.addLine(to: $0 ) }
    context.setLineWidth(3)
    context.setLineDash(phase: 0, lengths: [2])
    UIColor.lightGray.setStroke()
    context.strokePath()
    }

    View Slide

  29. func drawLinesBetween(_ context: CGContext, _ points: [CGPoint]) {
    points.first.flatMap { context.move(to: $0) }
    points.dropFirst().forEach { context.addLine(to: $0 ) }
    context.setLineWidth(3)
    context.setLineDash(phase: 0, lengths: [2])
    UIColor.lightGray.setStroke()
    context.strokePath()
    }

    View Slide

  30. func drawLinesBetween(_ context: CGContext, _ points: [CGPoint]) {
    points.first.flatMap { context.move(to: $0) }
    points.dropFirst().forEach { context.addLine(to: $0 ) }
    context.setLineWidth(3)
    context.setLineDash(phase: 0, lengths: [2])
    UIColor.lightGray.setStroke()
    context.strokePath()
    if timeValue > 0 {
    interpolateBetween(context, points)
    }
    }

    View Slide

  31. func interpolateBetween(_ context: CGContext, _ points: [CGPoint]) {
    guard points.count >= 2 else { return }
    var interpolated: [CGPoint] = []
    for i in 0 ..< points.count-1 {
    let p1 = points[i]
    let p2 = points[i+1]
    let t = p1.lerp(p2, t: timeValue)
    interpolated.append(t)
    drawPoint(context, p: t, color: .lightGray, radius: 6)
    }
    }

    View Slide

  32. func interpolateBetween(_ context: CGContext, _ points: [CGPoint]) {
    guard points.count >= 2 else { return }
    var interpolated: [CGPoint] = []
    for i in 0 ..< points.count-1 {
    let p1 = points[i]
    let p2 = points[i+1]
    let t = p1.lerp(p2, t: timeValue)
    interpolated.append(t)
    drawPoint(context, p: t, color: .lightGray, radius: 6)
    }
    }

    View Slide

  33. func interpolateBetween(_ context: CGContext, _ points: [CGPoint]) {
    guard points.count >= 2 else { return }
    var interpolated: [CGPoint] = []
    for i in 0 ..< points.count-1 {
    let p1 = points[i]
    let p2 = points[i+1]
    let t = p1.lerp(p2, t: timeValue)
    interpolated.append(t)
    drawPoint(context, p: t, color: .lightGray, radius: 6)
    }
    }

    View Slide

  34. func interpolateBetween(_ context: CGContext, _ points: [CGPoint]) {
    guard points.count >= 2 else { return }
    var interpolated: [CGPoint] = []
    for i in 0 ..< points.count-1 {
    let p1 = points[i]
    let p2 = points[i+1]
    let t = p1.lerp(p2, t: timeValue)
    interpolated.append(t)
    drawPoint(context, p: t, color: .lightGray, radius: 6)
    }
    }

    View Slide

  35. func interpolateBetween(_ context: CGContext, _ points: [CGPoint]) {
    guard points.count >= 2 else { return }
    var interpolated: [CGPoint] = []
    for i in 0 ..< points.count-1 {
    let p1 = points[i]
    let p2 = points[i+1]
    let t = p1.lerp(p2, t: timeValue)
    interpolated.append(t)
    drawPoint(context, p: t, color: .lightGray, radius: 6)
    }
    }

    View Slide

  36. func interpolateBetween(_ context: CGContext, _ points: [CGPoint]) {
    guard points.count >= 2 else { return }
    var interpolated: [CGPoint] = []
    for i in 0 ..< points.count-1 {
    let p1 = points[i]
    let p2 = points[i+1]
    let t = p1.lerp(p2, t: timeValue)
    interpolated.append(t)
    drawPoint(context, p: t, color: .lightGray, radius: 6)
    }
    }

    View Slide

  37. func interpolateBetween(_ context: CGContext, _ points: [CGPoint]) {
    guard points.count >= 2 else { return }
    var interpolated: [CGPoint] = []
    for i in 0 ..< points.count-1 {
    let p1 = points[i]
    let p2 = points[i+1]
    let t = p1.lerp(p2, t: timeValue)
    interpolated.append(t)
    drawPoint(context, p: t, color: .lightGray, radius: 6)
    }
    if interpolated.count >= 2 {
    drawLinesBetween(context, interpolated)
    }
    }

    View Slide

  38. zip(sequence1, sequence2)

    View Slide

  39. sequence1
    Sequence2

    View Slide

  40. sequence1
    Sequence2
    zip

    View Slide

  41. sequence1
    Sequence2
    zip

    View Slide

  42. sequence1
    Sequence2
    P1
    P2
    P3
    P4
    P5
    P2
    P3
    P4
    P5
    zip

    View Slide

  43. sequence1
    Sequence2
    P1
    P2
    P3
    P4
    P5 P2
    P3
    P4
    P5
    zip

    View Slide

  44. func interpZip(points: [CGPoint]) -> (CGFloat) -> [CGPoint] {
    return { t in
    let pairs = zip(points, points.dropFirst())
    return pairs.map { pair in
    return lerp(pair.0, pair.1, t)
    }
    }
    }

    View Slide

  45. func interpZip(points: [CGPoint]) -> (CGFloat) -> [CGPoint] {
    return { t in
    let pairs = zip(points, points.dropFirst())
    return pairs.map { pair in
    return lerp(pair.0, pair.1, t)
    }
    }
    }

    View Slide

  46. func interpZip(points: [CGPoint]) -> (CGFloat) -> [CGPoint] {
    return { t in
    let pairs = zip(points, points.dropFirst())
    return pairs.map { pair in
    return lerp(pair.0, pair.1, t)
    }
    }
    }

    View Slide

  47. func interpZip(points: [CGPoint]) -> (CGFloat) -> [CGPoint] {
    return { t in
    let pairs = zip(points, points.dropFirst())
    return pairs.map { pair in
    return pair.0.lerp(pair.1, t: timeValue)
    }
    }
    }

    View Slide

  48. View Slide

  49. Rapid Feedback

    View Slide

  50. Explore!
    Experiment!

    View Slide

  51. Thank you !
    Ben Scheirman
    nsscreencast.com
    @subdigital

    View Slide

  52. Thank you !
    Ben Scheirman
    nsscreencast.com
    @subdigital
    Ask me for a sticker!
    ステッカーが欲しかったら話
    しかけてください!

    View Slide

  53. Thank you !
    Ben Scheirman
    nsscreencast.com
    @subdigital
    Ask me for a sticker!
    ステッカーが欲しかったら話
    しかけてください!

    View Slide