11

我正在寻找一种通过多个点制作直线曲线的方法。最好使用 3 个点,尽管我认为为了给进入点的线的角度提供上下文,可以说,可能需要更多的点来给曲线提供上下文。

一般来说,一个起点P1,一个控制点P2和一个终点P3,线应该从P1弯到P2,然后从P2弯到P3。

实际上,这是我想要达到的效果的完美示例:

欧文霍尔样条

如果我能做到这一点,我真的会永远感激不尽!

到目前为止,在 Java 中,我尝试过使用QuadCurve2D.DoubleCub icCurve2D.DoublePath2D.Double(将 curveTo 与 Path2D.Double 一起使用)之类的东西,但无济于事 - 绘制的曲线甚至没有接近通过指定的控制点。

这是迄今为止我尝试过的方法的图像:

在此处输入图像描述

这是我用来生成图像中的点和曲线的代码:

    Graphics2D g = (Graphics2D) window.getGraphics();
    g.setColor(Color.blue);
    int d = 4;

    // P0
    int x0 = window.getWidth()/8;
    int y0 = 250;
    g.drawString("P0", x0, y0 + 4*d);
    g.fillRect(x0, y0, d, d);

    // P1
    int x1 = (window.getWidth()/7)*2;
    int y1 = 235;
    g.drawString("P1", x1, y1 + 4*d);
    g.fillRect(x1, y1, d, d);

    // P2
    int x2 = (window.getWidth()/2);
    int y2 = 200;
    g.drawString("P2", x2, y2 - 2*d);
    g.fillRect(x2, y2, d, d);

    // P3
    int x3 = (window.getWidth()/7)*5;
    int y3 = 235;
    g.drawString("P3", x3, y3 + 4*d);
    g.fillRect(x3, y3, d, d);

            // P4
    int x4 = (window.getWidth()/8)*7;
    int y4 = 250;
    g.drawString("P4", x4, y4 + 4*d);
    g.fillRect(x4, y4, d, d);

    g.setColor(Color.cyan);
    QuadCurve2D quadCurve = new QuadCurve2D.Double(x0, y0, x2, y2, x4, y4);
    g.draw(quadCurve);


    g.setColor(Color.YELLOW);
    CubicCurve2D.Double cubicCurve = new CubicCurve2D.Double((double)x0, (double)y0, 
                                                             (double)x1, (double)y1, 
                                                             (double)x2, (double)y2, 
                                                             (double)x4, (double)y4);
    g.draw(cubicCurve);


    g.setColor(Color.red);      
    Path2D.Double path1 = new Path2D.Double();
    path1.moveTo(x1, y1);
    path1.curveTo(x0, y0, x2, y2, x4, y4);
    g.draw(path1);

我希望曲线通过点的原因是我想“平滑”我写的折线图上顶点之间的过渡。在任何人提到它之前,JFree Chart 不是一个选项。我知道使用了不同类型的曲线和样条曲线,但我没有太多运气来准确理解它们的工作原理或如何实现适合我需要的东西。

我将非常感谢提供的任何帮助 - 提前致谢。

4

3 回答 3

12

我认为您错过了控制点的概念。控制点通常不在路径本身上。相反,它们控制路径曲线在点之间的形状。有关完整详细信息,请参阅样条教程

现在到了手头的问题,曲线上有点,但没有实际的控制点。有一些技术,如 Cardinal Spline,用于派生控制点,然后传递给您提到的曲线绘制 API 之一。您可能需要该Path2D.Double选项,以便可以将各个曲线平滑地串在一起。

所以对于从 P1 到 P2 到 P3 的绘图,而不是

Path2D.Double path1 = new Path2D.Double();
path1.moveTo(x1, y1);
path1.curveTo(x0, y0, x2, y2, x4, y4);
g.draw(path1);

你要

Path2D.Double path1 = new Path2D.Double();
path1.moveTo(x1, y1);
path1.curveTo(cx1a, cy1a, cx1b, cy1b, x2, y2);
path1.curveTo(cx2a, cy2a, cx2b, cy2b, x3, y3);
g.draw(path1);

其中cxcy坐标是您的派生控制点,每个三次样条线段有两个控制点。可能,

cx1a = x1 + (x2 - x1) / 3;
cy1a = y1 + (y2 - y1) / 3;
cx1b = x2 - (x3 - x1) / 3;
cy1b = y2 - (y3 - y1) / 3;
cx2a = x2 + (x3 - x1) / 3;
cy2a = y2 + (y3 - y1) / 3;
cx2b = x3 - (x3 - x2) / 3;
cy2b = y3 - (y3 - y2) / 3;

这里的模式是,对于内部点(在这种情况下只有 P2),它之前和之后的控制点(c1b 和 c2a)被它之前和之后的点(P1 和 P3)之间的线的斜率偏移。对于边缘点,控制点基于该点与下一个最近点之间的斜率。

如果您有特定领域的信息,您可能会选择不同的控制点。例如,您可能希望将端点处的斜率强制为 0。

于 2013-04-07T17:20:34.623 回答
2

好吧,也许这会有所帮助:P

通过Catmull-Rom曲线示例相同原理不同语言... http://schepers.cc/svg/path/dotty.svg

于 2013-04-07T16:41:36.150 回答
2

基本上你要的是三次样条插值,我可以在网上找到这个程序Interp2.java。它实际上包括多项式样条和三次样条。

不幸的是,它是一个小程序,而不是一个实际的类,但您仍然可以查看代码以了解他们是如何做到的。这总是一件好事。

于 2013-04-07T17:11:17.007 回答