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 full-size slide

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

    View full-size slide

  3. Control points?!

    View full-size slide

  4. Paul de Casteljau

    View full-size slide

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

    View full-size slide

  6. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  11. 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 full-size slide

  12. 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 full-size slide

  13. 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 full-size slide

  14. 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 full-size slide

  15. 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 full-size slide

  16. 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 full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

  19. 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 full-size slide

  20. 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 full-size slide

  21. 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 full-size slide

  22. zip(sequence1, sequence2)

    View full-size slide

  23. sequence1
    Sequence2

    View full-size slide

  24. sequence1
    Sequence2
    zip

    View full-size slide

  25. sequence1
    Sequence2
    zip

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  28. 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 full-size slide

  29. 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 full-size slide

  30. 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 full-size slide

  31. 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 full-size slide

  32. Rapid Feedback

    View full-size slide

  33. Explore!
    Experiment!

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide