使用贝塞尔曲线绘制图形

pet*_*ust 5 graphics plot bezier

我有一系列(x0,y0)... (xn,yn)单调的点,x并希望使用贝塞尔曲线通过这些点绘制“最佳”曲线。这条曲线不应该太“锯齿状”(例如类似于连接点),也不应该太弯曲(绝对不是“倒退”)。我已经创建了一个原型,但想知道是否有客观的“最佳解决方案”。

我需要找到所有段的控制点xi,y1 x(i+1)y(i+1)。我目前对一个段的方法(端点除外)x(i), x(i+1)是:

  • 找到向量x(i-1)...x(i+1),对其进行归一化和缩放,factor * len(i,i+1)以给出前导控制点的向量
  • 找到向量x(i+2)...x(i),归一化并按比例缩放factor * len(i,i+1)以给出尾随控制点的向量。

我试过 factor=0.1(太锯齿)、0.33(太弯曲)和 0.20 - 大约正确。但是有没有更好的方法(比如)使二阶和三阶导数尽可能平滑。(我假设这样的算法是在图形包中实现的)?

如果需要,我可以发布伪/代码。这是三个图像(0.1/0.2/0.33)。控制点由直线显示:黑色(尾随)和红色(前导)

使用因子=0.1

使用因子=0.2

使用系数=0.33

这是当前的代码。它的目的是在没有-ing 的情况下Y针对X(monotonic X) 进行绘图close。我已经建立了自己的库来创建 SVG(首选输出);此代码为每个曲线段(control1、xcontrol2、end)创建三元组x,yin coordArray。开始由最后一个操作(移动或曲线)假定。它是 Java 但应该很容易解释(CurvePrimitive映射到立方,"d"是 SVG 中完整路径的字符串表示)。

        List<SVGPathPrimitive> primitiveList = new ArrayList<SVGPathPrimitive>();
    primitiveList.add(new MovePrimitive(real2Array.get(0)));
    for(int i = 0; i < real2Array.size()-1; i++) {
        // create path 12
        Real2 p0 = (i == 0) ? null : real2Array.get(i-1);
        Real2 p1 = real2Array.get(i);
        Real2 p2 = real2Array.get(i+1);
        Real2 p3 = (i == real2Array.size()-2) ? null : real2Array.get(i+2);
        Real2Array coordArray = plotSegment(factor, p0, p1, p2, p3);
        SVGPathPrimitive primitive = new CurvePrimitive(coordArray);
        primitiveList.add(primitive);
    }
    String d = SVGPath.constructDString(primitiveList);
    SVGPath path1 = new SVGPath(d);
    svg.appendChild(path1);


/**
 * 
 * @param factor to scale control points by
 * @param p0 previous point (null at start)
 * @param p1 start of segment
 * @param p2 end of segment
 * @param p3 following point (null at end)
 * @return
 */
private Real2Array plotSegment(double factor, Real2 p0, Real2 p1, Real2 p2, Real2 p3) {
    // create p1-p2 curve
    double len12 = p1.getDistance(p2) * factor;
    Vector2 vStart = (p0 == null) ? new Vector2(p2.subtract(p1)) : new Vector2(p2.subtract(p0));
    vStart = new Vector2(vStart.getUnitVector().multiplyBy(len12));
    Vector2 vEnd = (p3 == null) ?  new Vector2(p2.subtract(p1)) : new Vector2(p3.subtract(p1));
    vEnd = new Vector2(vEnd.getUnitVector().multiplyBy(len12));
    Real2Array coordArray = new Real2Array();
    Real2 controlStart = p1.plus(vStart);
    coordArray.add(controlStart);
    Real2 controlEnd = p2.subtract(vEnd);
    coordArray.add(controlEnd);
    coordArray.add(p2);
    // plot controls
    SVGLine line12 = new SVGLine(p1, controlStart); 
    line12.setStroke("red");
    svg.appendChild(line12);
    SVGLine line21 = new SVGLine(p2, controlEnd); 
    svg.appendChild(line21);
    return coordArray;
}
Run Code Online (Sandbox Code Playgroud)

Zer*_*ero 3

贝塞尔曲线需要数据点以及每个点的斜率和曲率。在图形程序中,斜率由控制线的斜率设置,曲率由长度可视化。

当用户没有输入此类控制线时,您需要估计每个点的梯度和曲率。维基百科页面http://en.wikipedia.org/wiki/Cubic_Hermite_spline,特别是“插值数据集”部分有一个直接采用这些值的公式。

通常,根据点估计这些值是使用有限差来完成的 - 因此您可以使用任一侧的点的值来帮助估计。这里唯一的选择是如何处理只有一个相邻点的端点:您可以将曲率设置为零,或者如果曲线是周期性的,您可以“环绕”并使用最后一个点的值。

我引用的维基百科页面也有其他方案,但大多数其他人都介绍了一些其他“自由参数”,您需要找到一种设置方式,因此在没有更多信息来帮助您决定如何设置其他参数的情况下,我'我会选择简单的方案,看看您是否喜欢结果。

如果维基百科文章不够清楚,请告诉我,我会敲一些代码。

另一点需要注意:您想要哪种“类型”的贝塞尔插值?大多数图形程序都会在二维上进行三次贝塞尔曲线(即您可以绘制一条圆形曲线),但您的示例图像看起来可能是一维函数近似(因为对于每个 x 都只有一个 y 值)。我引用的页面上并没有真正提到图形程序类型曲线。将斜率和曲率的估计转换为http://en.wikipedia.org/wiki/B%C3%A9zier_curve (三次贝塞尔曲线)所示形式的控制向量所涉及的数学需要一些计算,但想法是相似的。

下面是一个可能方案的图片和算法,假设您唯一的输入是三个点 P1、P2、P3

贝塞尔插值方案

构造一条线 (C1,P1,C2),使得角度 (P3,P1,C1) 和 (P2,P1,C2) 相等。以类似的方式构建其他深灰色线条。这些深灰色线的交点(标记为 C1、C2 和 C3)成为控制点,与贝塞尔曲线维基百科网站上的图像具有相同的意义。因此,每条红色曲线(例如(P3,P1))都是由点(P3,C1,P1)定义的二次贝塞尔曲线。红色曲线的构造与维基百科网站上给出的相同。

但是,我注意到贝塞尔曲线维基百科页面上的控制向量似乎与您正在使用的控制向量类型不匹配,因此您可能必须弄清楚如何使这两种方法相等。