Java 如何使用Bezier曲线操纵端点角度

Java 如何使用Bezier曲线操纵端点角度,java,swing,geometry,bezier,Java,Swing,Geometry,Bezier,我想在两个形状之间生成一条贝塞尔曲线(在本问题中为矩形),以便曲线与形状成90度角。以下是MS Paint生成的示例: 出于我的目的,可以假设角度只需要垂直于轴,而不是任意的。换句话说,曲线在端点处的角度只需要为0、90、180或270度 我对贝塞尔曲线的理解告诉我,我需要2个端点和2个控制点。端点很容易计算,但我对如何操作控制点只有基本的了解 是否有一个方便的公式来实现这一点?我很难找到相关的教程 我正在使用Java,目前正在使用Java.awt.geom.Path2D.Double.cur

我想在两个形状之间生成一条贝塞尔曲线(在本问题中为矩形),以便曲线与形状成90度角。以下是MS Paint生成的示例:

出于我的目的,可以假设角度只需要垂直于轴,而不是任意的。换句话说,曲线在端点处的角度只需要为0、90、180或270度

我对贝塞尔曲线的理解告诉我,我需要2个端点和2个控制点。端点很容易计算,但我对如何操作控制点只有基本的了解

是否有一个方便的公式来实现这一点?我很难找到相关的教程

我正在使用Java,目前正在使用Java.awt.geom.Path2D.Double.curveTo()绘制曲线。如果有一个预构建的Java类或方法来实现这一点,那将是理想的,但如果需要,我愿意自己实现一个算法或等式。

智能使用应该能够实现您想要的:CubicCurve2D类实现
形状
接口。此类表示
(x,y)
坐标空间中的立方参数曲线段
CubicCurve2D.Float
CubicCurve2D.Double
子类在
Float
Double
精度中指定一条三次曲线

该类的
设置曲线(x1、y1、ctrlx1、ctrly1、ctrlx2、ctrly2、x2、y2)允许设置两个控制点。如果我们在曲线开始
(ctrlx1,ctrly1)
之前和曲线结束
(ctrlx2,ctrly2)
之后设置控制点。要将曲线角度保持为90度的倍数, 我们可以按如下类似方式计算控制点(针对
90度
)计算):

在下面的例子中,我假设δ=10

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g); 

    Graphics2D g2d = (Graphics2D) g.create();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    CubicCurve2D c = new CubicCurve2D.Double();
    int x1 = 150, y1 = 150;  //p1
    int x2 = 350, y2 = 300;//p3
    int ctrlx1, ctrly1, ctrlx2, ctrly2;
    int delta = 10;

    ctrlx1 = x1; // curve start x
    ctrly1 = y2 - delta; // curve start y
    ctrlx2 = x1 + delta; // curve end x
    ctrly2 = y2; 

    g2d.drawRect(x1-50, y1-100, 100, 100);
    c.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
    g2d.drawRect(x2, y2-50, 100, 100);
    g2d.draw(c);
}


根据Sage的优秀回答,在玩了一段时间后,我得出了以下结论

将控制点与要创建的角度共线会将曲线拉向该方向。控制点离端点越远,效果越明显。因为我只处理90度角,这意味着我可以改变控制点的x或y坐标一个设定的距离(我称之为delta,就像Sage那样),以产生所需的效果。我没有用任何花哨的方法来寻找delta——我只是尝试了几个值,直到找到一个我满意的值

下面是我最后的代码片段

public class BezierCurve {
    private CubicCurve2D curve;

    private static final int delta = 100;

    private double x1, y1, x2, y2;
    private double ctrlx1, ctrly1, ctrlx2, ctrly2;

    public BezierCurve(Point p1, Side side1, Point p2, Side side2) {
        this.x1 = p1.x;
        this.y1 = p1.y;
        this.x2 = p2.x;
        this.y2 = p2.y;

        Point ctrl1 = getControlPoint(p1, side1);
        Point ctrl2 = getControlPoint(p2, side2);

        ctrlx1 = ctrl1.x;
        ctrly1 = ctrl1.y;
        ctrlx2 = ctrl2.x;
        ctrly2 = ctrl2.y;

        curve = new CubicCurve2D.Double();
        curve.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
    }

    private Point getControlPoint(Point p, Side s) {
        int x = p.x;
        int y = p.y;

        switch (s) {
        case Left:
            x -= delta;
            break;
        case Right:
            x += delta;
            break;
        case Bottom:
            y += delta;
            break;
        case Top:
            y -= delta;
            break;
        }
        return new Point(x, y);
    }

    public void draw(Graphics2D g2) {
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.draw(curve);
    }
}
这将处理顶部、底部、右侧和左侧的每种情况

下面是代码的截图


这很有效,但我真的不明白为什么。你介意提供更多的解释或链接吗?具体来说,
delta
是什么?@kvother,delta只不过是控制点到圆角的距离。对于两个连接的边(曲线出现的地方),距离被设置为相等,这样我们就可以使垂直(0,90,270)form@Kvothe,问题解决了吗?我想顺便过来一下。是的,我让它工作了。我一直想跟进一段时间,但生活变得忙碌起来。非常感谢你的回答,这是无价的。我把我的最终解决方案贴出来作为答案。很抱歉没有给你这个名声,但我觉得从长远来看,一个更全面的答案是最好的。@Kvothe,:)没问题。我也很忙,当时正在写答案。我们都有时间做我们自己的重要工作链接到您的源参考不再有效。如果您发布了生成屏幕截图的完整代码示例,那就太好了。
public class BezierCurve {
    private CubicCurve2D curve;

    private static final int delta = 100;

    private double x1, y1, x2, y2;
    private double ctrlx1, ctrly1, ctrlx2, ctrly2;

    public BezierCurve(Point p1, Side side1, Point p2, Side side2) {
        this.x1 = p1.x;
        this.y1 = p1.y;
        this.x2 = p2.x;
        this.y2 = p2.y;

        Point ctrl1 = getControlPoint(p1, side1);
        Point ctrl2 = getControlPoint(p2, side2);

        ctrlx1 = ctrl1.x;
        ctrly1 = ctrl1.y;
        ctrlx2 = ctrl2.x;
        ctrly2 = ctrl2.y;

        curve = new CubicCurve2D.Double();
        curve.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
    }

    private Point getControlPoint(Point p, Side s) {
        int x = p.x;
        int y = p.y;

        switch (s) {
        case Left:
            x -= delta;
            break;
        case Right:
            x += delta;
            break;
        case Bottom:
            y += delta;
            break;
        case Top:
            y -= delta;
            break;
        }
        return new Point(x, y);
    }

    public void draw(Graphics2D g2) {
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.draw(curve);
    }
}