如何在 UIBezierPath 上绘制文本? [英] How to draw a text on a UIBezierPath?

查看:50
本文介绍了如何在 UIBezierPath 上绘制文本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不知道这是否可能.到目前为止,我的研究没有给出任何提示.

I don't know if that is possible. My researches so far, did not give any hints.

我有一个 UIBezierPath 并且喜欢在它旁边画一个文本,所以文本是弯曲的.

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 ?

推荐答案

参见 CurvyText 来自 iOS Programming Pushing the Limits 的一个简单版本,它沿着单个三次贝塞尔曲线绘制.该技术可以扩展到更复杂的曲线,但它确实变得更加复杂,尤其是在连接处.

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;
}

其他可能的 UIBezierPath 函数通常更简单.

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.

顺便说一句,你会注意到我做的事情像 (1-t)*(1-t)*(1-t) 而不是 pow(1-t,3).前者明显快于后者.有关更多讨论,请参阅Fast Bezier 简介.编译器足够聪明,只计算 1-t 一次,所以通常不需要创建临时变量,除非它有助于可读性.

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.

一旦你有了积分,你还有一个小问题.Bézier 是根据参数t 定义的,它是插值比.它不会映射到沿曲线的距离(这是您真正想要的).如果您在构建贝塞尔曲线中观看动画,请访问维基百科,希望它会更清楚为什么这是真的.

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;
}

有了这些,您必须布置文本.这需要让 Core Text 将文本线性布局,然后调整布局以将其沿曲线放置.我在 在曲线上绘制文本并为其设置动画的最高效方法?(该问题不需要介绍计算位置).

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).

这篇关于如何在 UIBezierPath 上绘制文本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆