• 贝塞尔曲线递归


    下面的说明只注重对概念的表述和对实际操作过程的说明,并不进行严格的证明。

    一般的贝塞尔曲线:
        对给定的 n+1 个点,可以作出 n 阶的贝塞尔曲线。其中最前和最后这两个点在曲线上,其余 n-1 个中间点是控制点,主要用于控制曲线的形状,不一定在曲线上。
        假如给定的 n+1 个点是 (x0,y0),...,(xn,yn), 以 t 作为参数,则生成的曲线上的所有点可以表示如下:
           n
      x =  ∑ C(n,i) *t^i *(1-t)^(n-i) *xi
          i=0

           n
      y =  ∑ C(n,i) *t^i *(1-t)^(n-i) *yi
          i=0
        其中 x,y 分别是生成的曲线上的点的 横坐标 和 纵坐标。
        t 是参数,其有效范围是 [0,1]。
        C(n,i)就是组合数,比如 C(3,1) = 3。


    实用 3 阶贝塞尔曲线:
        实际应用中,常用 3 阶的贝塞尔曲线,也就是要对 4 个点进行计算。
    设 已知 的4点为 p0(x0,y0),p1(x1,y1),p2(x2,y2),p3(x3,y3),其中 0 和 3 是端点, 1,2是控制点,则 该曲线上的所有点表示为:
      x = (1-t)^3 *x0 + 3*t*(1-t)^2 *x1 + 3*t^2*(1-t) *x2 + t^3 *x3
      y = (1-t)^3 *y0 + 3*t*(1-t)^2 *y1 + 3*t^2*(1-t) *y2 + t^3 *y3
      其中 0 <= t <= 1.


    直接的简单实现:
        按照公式,对每个t,代入式子就可以得到曲线上对应点的坐标了。当然具体实现时,可根据适当的间隔取  t 值,然后用直线将这些值对应的点连起来就是了。
        类似于多项式的计算,在计算机上计算的时候,可以用 德卡斯特里奥算法 来求出,而不必直接计算高次幂。
    设四个控制顶点是p0,p1,p2,p3,对每个具体的 t,可以做出下面的表:
    p00
    p10  p11
    p20  p21  p22
    p30  p31  p32  p33

    其中:
    p00 = p0
    p10 = p1
    p20 = p2
    p30 = p3

    p11 = p00*(1-t) + p10*t
    p21 = p10*(1-t) + p20*t
    p31 = p20*(1-t) + p30*t

    p22 = p11*(1-t) + p21*t
    p32 = p21*(1-t) + p31*t

    p33 = p22*(1-t) + p32*t
    通过验证可以知道,p33就是要求的值。

    代码略。


    简单实现遇到的问题:
        上面说的简单实现,t 的间距不好确定。因为曲线的形状可能千奇百怪,间隔太大则误差太大,太小的话则效率太低,难以确定一个对所有情况都合适的间隔值。而在实际应用中,则是用 递归 的方法来解决这个问题的。


    实际中的递归处理:
        回头看上面求值的那个算法,当取 t=1/2 时, t = 1-t = 1/2,计算过程可以简化如下:
    p00
    p10  p11
    p20  p21  p22
    p30  p31  p32  p33
    其中:
    pi0跟上面一样,分别就是那4个输入点p0,p1,p2,p3
    pij = [ p(i-1)(j-1) + p(i-1)j ] / 2    (简化的就是这个计算过程)

    显然,p33就是 t=1/2 时所对应曲线上的点。
    而且,
        如果以p00,p11,p22,p33为4个新的输入点,生成新的3阶贝塞尔曲线,则该曲线跟原来p0~p3生成的曲线在t∈[0,1/2]的部分“完全重合”。
        如果以p33,p32,p31,p30为4个新的输入点,生成新的3阶贝塞尔曲线,则该曲线跟原来p0~p3生成的曲线在t∈[1/2,1]的部分“完全重合”。
    这里注意新输入点的输入顺序。

        上面漏了说贝塞尔曲线跟输入点的一个关系,就是贝塞尔曲线总是处在 输入点的凸包 内。那么可以得知,曲线上的点到 线段p0p3(就是两个端点) 的距离不大于 max(p1到p0p3的距离, p2到p0p3的距离)。
        而采用 德卡斯特里奥算法,取t=1/2 所求出来的中间点pij,其实就是 p(i-1)(j-1),pi(j-1) 的中点。比划一下也可以看出,中间点全部都在 p00,p10,p20,p30的凸包里面,也就是 p00,p11,p22,p33所形成的凸包 和 p33,p32,p21,p30的凸包也被包含在 p0,p1,p2,p3 的凸包里面。

    综上所述:
        p00,p11,p22,p33 和 p33,p32,p31,p30 所生成的曲线拼起来,就是p0,p1,p2,p3生成的曲线。
        对p0,p1,p2,p3,用线段 p0p3 代替 生成的曲线,并以曲线上的点到线段的最大距离为误差,则 p00,p11,p22,p33 和 p33,p32,p31,p30的误差比 p0,p1,p2,p3 的要小。

    所以,得到的递归生成曲线步骤如下:
    1、
        对 p0,p1,p2,p3 4个点,先检查 
         max( d(p1, p0p3), d(p2, p0p3) ) < E (d()是点到直线距离,E是给定的误差范围,表示p0,p2,p3,p3的凸包的边跟生成曲线的最大差距。) 
        是否成立。
        如果 满足 条件,则简单用线段 p0p3 代替生成的曲线,返回。
        如果不满足,继续。

    2、
        对 p0,p1,p2,p3 4个点,进行 t=1/2 的德卡里奥算法。
        对得到的 p00,p11,p22,p33,递归进行步骤 1 的操作;
        对得到的 p33,p32,p31,p30,递归进行步骤 1 的操作。
        返回。
  • 相关阅读:
    POJ-2253 Frogger---最短路变形&&最大边的最小值
    POJ-2263 Heavy Cargo---最短路变形&&最小边的最大值
    POJ-2570 Fiber Network---Floyd+二进制表示集合
    POJ-3259 Wormholes---SPFA判断有无负环
    POJ-3268 Silver Cow Party---正向+反向Dijkstra
    Floyd算法
    View相关面试问题-ListView缓存面试问题讲解
    View相关面试问题-事件分发面试问题讲解
    Android基础相关面试问题-binder面试问题详解
    Android异常与性能优化相关面试问题-其他优化面试问题详解
  • 原文地址:https://www.cnblogs.com/lizhengjin/p/1827813.html
Copyright © 2020-2023  润新知