3

Given an arbitrary UIBezierPath, I'm looking for a way to get a point at a fraction of the length of that path.

Example:

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(200.0, 200.0)];
[path addLineToPoint:CGPointMake(200.0, 400.0)];

CGPoint p = [path pointAtFraction:0.5];

p should be {x: 200.0, y: 300.0} in this case.

I'm aware that this simple example could be calculated, but I'm looking for a solution that fits ANY UIBezierPath (arcs, rounded rects, etc.)

Seeing CAShapeLayer, which basically lives off UIBezierPath, and its property strokeEnd, I suppose the information is somewhere inside the path object. However, neither UIBezierPath nor CGPathRef interfaces show any way to achieve this.

I tried creating a CAShapeLayer, setting the strokeEnd and retrieving the CGPathRef from the layer, but the path stays the same.

Is there any way (public API) to achieve this ?

CodingMeSwiftly
  • 3,231
  • 21
  • 33
  • I don't think there is a public API for that. What you are looking for is the "arc length parametrisation" of bezier paths. – Martin R Aug 02 '14 at 17:28
  • Hm yes. thanks for your hint. I guess thats higher mathematics ... any chance an average software engineer (me ;)) might implement this on his own, without losing his head ? :) – CodingMeSwiftly Aug 02 '14 at 17:41
  • This is related: http://stackoverflow.com/questions/12024674/get-cgpath-total-length, because computing the arc length would be the first step. – Martin R Aug 02 '14 at 17:56
  • Hey @Cabus , forgot the detailed answer you promise below :) Could you please share the solution? I'm needing exactly the same. Thanks! – Omer Sep 25 '15 at 20:45
  • sorry for the delay. i posted the answer ;) – CodingMeSwiftly Sep 26 '15 at 20:08

2 Answers2

8

Some time ago I've made a carousel menu based on bezier cubic curve. I think this code can help you:

CGFloat bezierInterpolation(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) {
    CGFloat t2 = t * t;
    CGFloat t3 = t2 * t;
    return a + (-a * 3 + t * (3 * a - a * t)) * t
           + (3 * b + t * (-6 * b + b * 3 * t)) * t
           + (c * 3 - c * 3 * t) * t2
           + d * t3;
}

- (CGPoint)getPointForT:(CGFloat)t {
    return CGPointMake(bezierInterpolation(t, _p1.x, _p2.x, _p3.x, _p4.x), bezierInterpolation(t, _p1.y, _p2.y, _p3.y, _p4.y));
}

t - your fraction length <0,1>

_p1, _p2, _p3, _p4 - curve's points (check this site to visualize it a little bit: http://cubic-bezier.com/#.17,.67,.83,.67)

ArturOlszak
  • 2,653
  • 2
  • 21
  • 20
  • YES! Thank you so much for this piece of code! It pushed me in the right direction ;). - To be honest, at first I thought the code is useless in my case, but after some research i understood why it is exactly what the at the core of this problem. I'll post a very detailed answer tomorrow. I'm also going to 'publish' a category which will make this thing easy to use :). - Until I post my final answer on this, I'll mark your answer as the correct one, as it gives a great hint for where to begin with this problem. Thank you ! – CodingMeSwiftly Aug 03 '14 at 22:07
  • Hey, I'm having the same problem, could you please share your solution? @Cabus – Omer Sep 25 '15 at 21:00
  • sorry for the delay. i posted the answer ;) – CodingMeSwiftly Sep 26 '15 at 20:08
  • _p[1-4] are curve points where p1 is start, p2 is end and 3, 4 are control points? Or start, cp1, cp2, end? – Vitaly Jul 29 '16 at 09:27
4

Sorry for the huge delay. I actually solved this more than 1 year ago, wow.
I created a small category on UIBezierPath which does the job. GitHub Repo here

After re-reading my code from 1 year back I have to say it's quite shocking how I used to code back then :D The lack of documentation is disturbing. If you need more info please let me know and I'll update the repo.

CodingMeSwiftly
  • 3,231
  • 21
  • 33
  • It's kinda lame that you have marked you're own answer as correct, one year after my answer, which gave you the solution.. But whatever ;-) – ArturOlszak Sep 27 '15 at 07:39
  • Thanks for this answer, and for supplying such a wonderful category to support it! This is half of exactly what I need. The only thing left for me is the opposite: I want a method `- (CGFloat)percentOfPathAtPoint(CGPoint point)`. My math is so weak in this area, so any pointers would be appreciated... – Aaron Vegh Nov 06 '15 at 16:31
  • The answer you gave to yourself was exactly what I was going to tell you :) I'm afraid there is no better solution for this :/ – CodingMeSwiftly Nov 24 '15 at 10:00