Fast, crude version of atan2() for C/Objective-C/Swift?

679 views Asked by At

I'm working on some graphics code that does live drawing of smooth, continuous curves. I want to add support for feathered brushes. For that, I need to be able to calculate "normals", or lines perpendicular to, the line segments that make up my curve.

The pure math way to do that is to use arctan to find the angle of a segment, rotate 90 degrees, and use sine and cosine to find the x and y offsets of my normals. Once I have my angles it would be pretty easy to use a lookup table to replace sine and cosine, but writing a performant, low precision version of atan2() seems tricky.

The lengths and angles of my normals (perpendicular lines) doesn't need to be exact. If it's off by a tenth it won't matter.

Have any of you developed a high speed, crude version of atan2() for this sort of graphics work? Optimizing this sort of thing for performance is fussy and time-consuming.

I'm working in Swift 3, but am "multi-lingual". I'm fine with integrating code written in C/Objective-C as well. (or perhaps converting it to Swift.)

EDIT:

More details:

The project involves taking freehand drawing, which provides a series of points that are sometimes fairly far apart if the user drags their finger quickly, and using Catmull-Rom splines to add intermediate points to create a series of line segments small enough that they look like a smooth curve.

(Catmull-Rom splines are a curve that is similar to the better-known Bezier curve, but all the control points for the curve lie on the curve, so it's straightforward to smooth a curve the user enters "freehand" using the input vertexes to do the smoothing.)

(From now on I'll refer to "curves", but what I really mean is polylines composed of line segments so short that they appear to be smooth curves)

I've factored my code so that I generate splines for only the parts of the curve that have changed. It's plenty fast now, and creates beautifully smooth curves as fast as you can draw. Subjectively, it seems that I'm drawing a curve that follows your finger track point by point, with no jumping between points.

The next goal is to be able to draw with a soft-edged brush. To do that, I want to find curves on the left and right of the user's finger-track that are parallel to the curve. I will then use OpenGL to create triangle strips that define the thick curve between the left and right lines, and use polygon shading to feather the curve from opaque along the user's finger-track curve to transparent along the left and right parallel curves.

Say I want to draw a soft-edged curve that's 6 points thick. The curves that follow the user's finger-track on the left and on the right would each need to extend 3 points from the user's curve.

I'm planning to find the vertexes of the end-points for the left and right curves by finding line segments perpendicular to the line segments of the user's finger-track that pass through the vertexes of the finger-track, and extend by 1/2 of the desired line thickness to the left and right of the user's finger-track.

Below is a screen-shot of a curve drawn by the current version of the program, with the input vertexes drawn as blue diamonds, and the smoothing points I add drawn as hollow squares. (I've decreased the number of added smoothing points so you can better see what's going on.)

Imagine drawing a series of "hash marks" through each of the vertex points, each 6 points long, centered on one of those vertex points, and perpendicular to the line first line segment that of the smoothed curve that ends at that vertex.

As Peter O. pointed out in his answer, finding a line segment perpendicular to any particular line segment is easy - you just invert the slope. However, I want line segments of a specific length. (In my example, 6 points, 3 points on either side of a vertex.) I'm looking for way to do that fast. I could figure out the end-points of my normals using either trig or square roots, both of which are really slow.

enter image description here

1

There are 1 answers

8
Peter O. On BEST ANSWER

For your problem, if all you want is to find a line that's perpendicular to another, you don't even need to use atan2; you can use the following approach instead. Let (x1, y1) and (x2, y2) be the input line segment.

// Find deltas
dx=x2-x1
dy=y2-y1
// Find perpendicular vector to (dx, dy)
 pdx=-dy
 pdy=dx
 // Normalize the vector to a unit vector
 length=sqrt(pdx*pdx+pdy*pdy)
 if(length!=0){
    invlength=1.0/length
// Scale the vector as desired
    invlength*=scale
    pdx*=invlength
   pdy*=invlength
 }
 // Now, find a line segment parallel to the vector
 x2=x1+pdx
 y2=y1+pdy

Alternatively, if you insist on atan2, I am aware of one public domain implementation at http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization. Try both approaches and see which suits your purpose better.

In general, if you find yourself calling atan2(dy,dx) just to get the proper angle for cos and sin, you can often just use the x and y components of the normalized (dx, dy) vector to get the cosine and sine instead, respectively.