Reversing Bezier Curves

bp1

 

bp2

 

It turns out that reversing a path is a lot trickier than it first appears. Sadly, the built in UIBezierPath option bezierPathByReversingPath is broken. Here’s what Xcode reports when that method is called on the Hello path you see above:

HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b6c4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b784: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b794: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b7e4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b7f4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b814: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
Apr  8 09:04:30 Esopus-Spitzenburg.local HelloWorld[62165] : void CGPathAddLineToPoint(CGMutablePathRef, const CGAffineTransform *, CGFloat, CGFloat): no current point.

And here’s what the path created by bezierPathByReversingPath looks like:

iOS Simulator Screen shot Apr 8, 2013 9.04.34 AM

It’s not exactly the result you expect.

To create a solution, I ended up writing my own Bezier library, where I decompose each path into first, its subpaths (each subpath begins with a Move operation, and may or may not end with a Close operation), and then into the component elements.

Reversing paths turns out to be a bit trickier than I anticipated. That’s because when you reverse items, you have to take close points into account. That means a reversed path often looks like this:

  • Move to the destination point of 2nd to last element (the one that comes before the close command)
  • Add a line to the first element of the path
  • Reverse the each line and curve, using the destination for each item as the start point, and the start element as the destination
  • If you had a close command in the original path, apply that close to avoid odd line cap artifacts

Here’s the code in question. It’s still got new code smell all over it, so if you find anything I messed up please let me know. Also, it uses quite a few custom subclasses and routines, which I’ve omitted to focus on the approach rather than the details.

Comments are closed.