我不知道这是否可行。到目前为止,我的研究没有给出任何暗示。
我有一个UIBezierPath,并且喜欢绘制文本,因此文本是弯曲的。
我有可能吗?任何提示从哪里开始?
答案 0 :(得分:1)
请参阅iOS编程推送限制中的CurvyText,了解一个简单版本,该版本沿着单个立方贝塞尔曲线绘制。该技术可以扩展到更复杂的曲线,但它确实变得更加复杂,特别是在交叉点。
您需要几种基本技巧。首先,您需要一个函数来计算曲线上点的位置。然后,您需要计算函数的一阶导数(斜率)。然后,您需要一种将线性距离转换为偏移的方法。最后,您需要布局文本。
计算曲线上的点可能是最简单的部分。你只需要计算功能。例如,这是立方贝塞尔函数:
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;
}
其他可能的UIBezierPath
函数通常更简单。
然后你需要计算曲线的斜率。这只是第一个衍生物,希望你可以从第一级微积分的记忆中挖掘出来。
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;
}
如果这是一条更复杂的曲线,则在交汇处未定义斜率。因此,如果您遇到这个问题,您可能希望将偏移量略微加减,以避免尝试在过渡时准确布置字母。
顺便说一句,你会发现我做的事情比(1-t)*(1-t)*(1-t)
而不是pow(1-t,3)
。前者比后者快得多。有关详细信息,请参阅Introduction to Fast Bezier。编译器非常智能,只能计算1-t
一次,因此通常不需要创建临时变量,除非它有助于提高可读性。
一旦获得积分,您仍然会遇到一个小问题。 Bézier是根据参数t
定义的,它是插值比率。它不会映射到曲线上的距离(这是您真正想要的)。如果你在维基百科的Constructing Bézier curves观看动画,希望它更清楚为什么会这样。
沿Bézier曲线的距离没有简单的闭合函数。在Moving Along a Curve with Specified Speed中有一些很好的数值方法,但是我发现对于这个特殊的问题,只是沿着曲线非常缓慢地移动,直到找到一个与原点相距所需线性距离的点。这并不能真正计算沿曲线的距离,但它很容易,快速,并且提供了良好的文本布局,特别是对于“合理的”曲线。
// 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;
}
有了这些,你必须列出文本。这需要要求Core Text线性布局文本,然后调整布局以将其放置在曲线上。我在Most performant way to draw text on a curve, and animate it?中讨论了这部分问题(该问题不需要介绍计算位置)。