问题
I don't know if that is possible. My researches so far, did not give any hints.
I have a UIBezierPath and like to draw a text alined it, so the text is curved.
I this possible ? Any hints where to start ?
回答1:
See CurvyText from iOS Programming Pushing the Limits for a simple version that draws along a single cubic Bézier curve. The technique can be extended to more complex curves, but it does become more complicated, particularly at the junctions.
There are several basic techniques you need. First, you need a function to compute the location of points on the curve. Then, you need to compute the first-derivative (slope) of the function. Then, you need a way to convert linear distance into an offset. And finally, you need to layout the text.
Computing the points on the curve is probably the easiest part. You just need to compute the function. For example, this is the cubic Bézier function:
static double Bezier(double t, double P0, double P1, double P2,
double P3) {
return
(1-t)*(1-t)*(1-t) * P0
+ 3 * (1-t)*(1-t) * t * P1
+ 3 * (1-t) * t*t * P2
+ t*t*t * P3;
}
The other possible UIBezierPath
functions are generally even simpler.
Then you need to compute the slope of the curve. That's just the first derivative, which hopefully you can dredge up from memories of first level calculus.
static double BezierPrime(double t, double P0, double P1,
double P2, double P3) {
return
- 3 * (1-t)*(1-t) * P0
+ (3 * (1-t)*(1-t) * P1) - (6 * t * (1-t) * P1)
- (3 * t*t * P2) + (6 * t * (1-t) * P2)
+ 3 * t*t * P3;
}
If this is a more complicated curve, the slope is undefined at the junctions. So if you have that problem, you would probably want to bump the offsets a little plus-or-minus to avoid trying to lay out a letter exactly at the transition.
BTW, you'll notice I do things like (1-t)*(1-t)*(1-t)
rather than pow(1-t,3)
. The former is dramatically faster than the latter. See Introduction to Fast Bezier for much more discussion of that. The compiler is smart enough to only compute 1-t
once, so there's generally no need to create temporary variables unless it helps readability.
Once you have the points, you still have a small problem. Bézier is defined in terms of a parameter t
, which is the interpolation ratio. It doesn't map to distance along the curve (which is what you really want). If you watch the animations in Constructing Bézier curves at Wikipedia, hopefully it'll be clearer why this is true.
There's no simple, closed-form function for distance along a Bézier curve. There are some good numerical methods for it in Moving Along a Curve with Specified Speed, but I've found that for this particular problem, just moving along the curve very slowly until you find a point that is the desired linear distance from the origin. This doesn't really calculate distance along the curve, but it's easy, fast, and gives good text layout, especially for "reasonable" curves.
// Simplistic routine to find the offset along Bezier that is
// aDistance away from aPoint. anOffset is the offset used to
// generate aPoint, and saves us the trouble of recalculating it
// This routine just walks forward until it finds a point at least
// aDistance away. Good optimizations here would reduce the number
// of guesses, but this is tricky since if we go too far out, the
// curve might loop back on leading to incorrect results. Tuning
// kStep is good start.
- (double)offsetAtDistance:(double)aDistance
fromPoint:(CGPoint)aPoint
andOffset:(double)anOffset {
const double kStep = 0.001; // 0.0001 - 0.001 work well
double newDistance = 0;
double newOffset = anOffset + kStep;
while (newDistance <= aDistance && newOffset < 1.0) {
newOffset += kStep;
newDistance = Distance(aPoint,
[self pointForOffset:newOffset]);
}
return newOffset;
}
With those in hand, you have to lay out the text. That requires asking Core Text to lay out the text linearly, and then adjusting the layout to place it along the curve. I covered that part of the issue in Most performant way to draw text on a curve, and animate it? (that question didn't need an introduction to computing the locations).
来源:https://stackoverflow.com/questions/33652153/how-to-draw-a-text-on-a-uibezierpath