1

I have a section in my app where users can draw something. Because of the slow refresh rate of touchesMoved, if a user draws fast, the line doesn't go smooth. That's why I'm using Hermite. It sets a point for every refresh rate and then draws a UIBezierpath (with extension to make it smooth) between those points. It's working perfectly ! I have a smooth design right now !

The only problem is that I'm sometimes not getting a round begin cap and if I switch on the same path back it also happens.

I think it has something to do with the code that draws a bezierpath between those points. So I'm looking for someone that also uses Hermite and knows how to fix this. enter image description here

MaximVW
  • 203
  • 1
  • 4
  • 12
  • Unrelated, but you say "Because of the slow refresh rate of `touchesMoved`...". Note that on physical device, you can use coalesced touches to reduce the gaps between points. It only works on devices, and even then, not in all cases, but for capable devices it results in a more accurate path. You're probably already doing that, but I mention it for the sake of future readers. – Rob Jul 05 '17 at 23:23

1 Answers1

2

The hermite spline is just a series of cubic beziers. But you can get these weird discrepancies with UIBezierPath. For example, there is a UIBezierPath rendering problem when you add multiple cubic beziers where the start, control point 1, control point 2, and end are all collinear. So, I've added a check to my hermite spline path to check to see if these four points are collinear, and, if so, just add a line in those cases.

So, instead of just:

addCurve(to: endPoint, controlPoint1: control1, controlPoint2: control2)

I do:

let angleFull = angle(between: previousPoint, and: endPoint)
let angle1 = angle(between: previousPoint, and: control1)
let angle2 = angle(between: control2, and: endPoint)

if (angleFull == angle1 || angle1 == nil) && (angleFull == angle2 || angle2 == nil) {
    addLine(to: endPoint)
} else {
    addCurve(to: endPoint, controlPoint1: control1, controlPoint2: control2)
}

Where

private func angle(between point1: CGPoint, and point2: CGPoint) -> CGFloat? {
    if point1 == point2 { return nil }
    return atan2(point2.y - point1.y, point2.x - point1.x)
}

Another, more generalized solution (though a bit inelegant, IMHO), approach, is to avoid joining these cubic curves entirely, so, before adding each cubic curve, do a move(to:) the previous point first. That should prevent all problems stemming from bugs related to joining cubic beziers.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Hello, I've tried this but I have still have the exact same problem. – MaximVW Jul 06 '17 at 07:59
  • Try not making them a single contiguous path (e.g. insert a `move(to: previousPoint)` before adding a curve to the path. It's inelegant, but more robust way to bypass bugs in `UIBezierPath` not knowing how to join cubic curves. – Rob Jul 06 '17 at 08:05