【问题标题】:Creating cubic and/or quadratic Bezier curves to fit a path创建三次和/或二次贝塞尔曲线以拟合路径
【发布时间】:2015-11-05 23:07:01
【问题描述】:

我正在使用 Lua 开发一款 2D 游戏。 我有一条由许多点组成的路径,例如,点 A 到 L:

我想沿着这条路径平稳地移动一个对象。为了实现这一点,我想根据这些点创建二次或三次贝塞尔曲线,并对这些曲线进行插值。但是,我如何正确拟合曲线,这样路径就不会被破坏,例如。一条曲线在 F 点停止,另一条曲线开始?

【问题讨论】:

  • 我认为大多数解决方案都会假设所有点的曲线都是平滑的,而不是像 F 点那样尝试检测中断。您可能需要在带外执行此操作,但这会呈现出自己的情况问题 - 您使用什么标准来确定休息时间?
  • 感谢您的回复。我不知道,我认为这是问题的根源。本质上,路径需要像点 F 弯曲的拐角。我是否需要找到一些旋转值来表明这一点?
  • 不,你没有。您想通过点制作一条平滑曲线,并且您可能希望最终使用 Beziers 进行渲染,但您最不想要的是 actual Bezier 曲线,它不通过点。您想要 Catmull-Rom 曲线,它与 Bezier 曲线相关,但 确实 会经过指定点。此外,您能否(在您的帖子中)描述“顺利”的含义?例如,如果您正在考虑固定速度,贝塞尔曲线会使这变得非常困难。最后:你能否展示一下你认为应该连接这些点的路径?因为这决定了合适的曲线拟合。
  • Interpolation polynomialProper implementation of cubic spline interpolation ...使用插值它通过点...如果需要贝塞尔曲线然后将控制点转换为贝塞尔曲线,链接中没有拟合所需的方程

标签: algorithm lua interpolation curve-fitting bezier


【解决方案1】:

曲线拟合在这个问题中解释:How can I fit a Bézier curve to a set of data?

但是,必须有一种更简单的方法来插入点。它是这样的:

  1. 细化:在每条边的中间放置一个点。
  2. 对偶:在每条边的中间放一个点,去掉旧的点。
  3. 重复 Dual 步骤数次。
  4. 从第一步开始重复,直到曲线足够平滑。

您可以很容易地看到它在纸上有效。生成的曲线开始逼近一系列贝塞尔样条曲线。

在此 PDF 文件(第 3 节)中有更正式的描述:http://www.cc.gatech.edu/~jarek/courses/handouts/curves.pdf

这是一些 Javascript 代码:

function bspline_smooth(points, order) {

    // insert a point in the middle of each edge.
    function _refine(points) {
      var i, index, len, point, refined;
      points = [points[0]].concat(points).concat(points[points.length-1]);
      refined = [];
      index = 0;
      for (i = 0, len = points.length; i < len; i++) {
        point = points[i];
        refined[index * 2] = point;
        if (points[index + 1]) {
          refined[index * 2 + 1] = _mid(point, points[index + 1]);
        }
        index += 1;
      }
      return refined;
    }

    // insert point in the middle of each edge and remove the old points.
    function _dual(points) {
      var dualed, i, index, len, point;
      dualed = [];
      index = 0;
      for (i = 0, len = points.length; i < len; i++) {
        point = points[i];
        if (points[index + 1]) {
          dualed[index] = _mid(point, points[index + 1]);
        }
        index += 1;
      }
      return dualed;
    }

    function _mid(a, b) {
      return new Point(
        a.x + ((b.x - a.x) / 2),
        a.y + ((b.y - a.y) / 2) );
    }

    if (!order) {
       return points;
    }
    return bspline_smooth(_dual(_dual(_refine(points))), order - 1);
}

【讨论】:

    【解决方案2】:

    你不需要知道数学就可以做到这一点。

    使用单独的贝塞尔曲线将每个点与下一个点连接起来。

    贝塞尔曲线的端点附有“手柄”。这些手柄与曲线相切(在端点处)。要制作一条“平滑”的路径,您需要做的就是让每两条相邻贝塞尔曲线的手柄“坐”在一条线上。

    要理解这一点,请尝试使用 GIMP 之类的绘图程序(但可能几乎任何其他软件都可以)。这样的程序甚至有一个特殊的键来使相邻曲线的“手柄”位于一条直线上(并且长度相同)。

    您必须做出的唯一“硬”数学决定是确定这些句柄的长度。实验。您可能希望使其取决于每 3 个连续点偏离直线的距离,或者取决于点之间的距离。

    最后,关于以看似恒定的速度沿曲线移动一个点(您的“对象”):您可以使用 like this one 方法的改编版。

    【讨论】:

    • 问题说“曲线”复数,特别提到二次和三次形式,这意味着 OP 已经知道所有这些。它并没有真正回答问题,缺少所有重要的细节。
    • @Mark:我看不出我的回答中有什么不同意他的问题。如果他想要在他的整体路径中出现“角落”,那么我已经回答过:“平滑”只有当手柄在同一条线上时。我还提到了一个关于“取决于每 3 个连续点偏离直线的距离”的提示(例如,在我们的例子中,点 E F G,所以他可以使手柄的长度为 F 零)。请参阅我的“了解这一点”段落。请不要让这看起来很复杂。
    • 我再说一遍,“所有重要的细节都不见了”。这个答案完全不足以帮助某人完成手头的任务,特别是如果他们已经知道你所说的一切。
    猜你喜欢
    • 1970-01-01
    • 2012-01-19
    • 2011-03-10
    • 1970-01-01
    • 2016-10-05
    • 2012-07-27
    • 2017-01-09
    • 2015-08-01
    • 1970-01-01
    相关资源
    最近更新 更多