【问题标题】:Verify if a point is part of quadratic Bezier curve in Java验证一个点是否是Java中二次贝塞尔曲线的一部分
【发布时间】:2013-06-15 03:16:59
【问题描述】:

我想验证一个点是否是由点 p0、p1 和 p2 定义的二次贝塞尔曲线的一部分..

这是我在曲线中获得具有一定 t 的点的函数:

public static final Point quadratic (Point p0, Point p1, Point p2, double t) {
    double x = Math.pow(1-t, 2) * p0.x + 2 * (1-t) * t * p1.x + Math.pow(t, 2) * p2.x;
    double y = Math.pow(1-t, 2) * p0.y + 2 * (1-t) * t * p1.y + Math.pow(t, 2) * p2.y;

    return new Point((int)x, (int)y);
}

考虑得到二次曲线中的点B(t)如下:

B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2

我应该通过获取该点的 t 值并将其与使用该 t 参数获得的点进行比较来验证点 P 是否属于曲线,但在 Java 中我遇到了变量精度问题。

我的验证点功能如下:

public static final boolean belongsQuadratic (Point p, Point p0, Point p1, Point p2) {
    double[] tx = obtainTs(p.x, p0, p1, p2);
    double[] ty = obtainTs(p.y, p0, p1, p2);

    if (tx[0] >= 0) {
        if ((tx[0] >= ty[0] - ERROR && tx[0] <= ty[0] + ERROR) || (tx[0] >= ty[1] - ERROR && tx[0] <= ty[1] + ERROR)) {
            return true;
        }
    }

    if (tx[1] >= 0) {
        if ((tx[1] >= ty[0] - ERROR && tx[1] <= ty[0] + ERROR) || (tx[1] >= ty[1] - ERROR && tx[1] <= ty[1] + ERROR)) {
            return true;
        }
}


    return false;
}

public static double[] obtainTs (int comp, Point p0, Point p1, Point p2) {
    double a = p0.x - 2*p1.x + p2.x;
    double b = 2*p1.x - 2*p0.x ;
    double c = p0.x - comp;

    double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
    double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);

    return new double[] {t1, t2};
}

所以如果我用这个值运行代码:

Point p0 = new Point(320, 480);
Point p1 = new Point(320, 240);
Point p2 = new Point(0, 240);
double t = 0.10f;
Point p = Bezier.quadratic(p0, p1, p2, t);
double[] ts = Bezier.obtainTs(p.x, p0, p1, p2);

我得到以下输出:

For t=0.10000000149011612, java.awt.Point[x=316,y=434]
For t1: -0.1118033988749895, java.awt.Point[x=316,y=536]
For t2: 0.1118033988749895, java.awt.Point[x=316,y=429]
java.awt.Point[x=316,y=434] belongs?: false

我应该使用BigDecimal 来执行操作吗?有没有另一种方法来验证这一点?谢谢

【问题讨论】:

  • 你不能使用Graphics2D 包来使用CubicCurve2D 方法吗?
  • @Sebastian 在我的问题中我使用二次函数,三次函数不起作用.. nachokk 我会试试 BigDecimal,谢谢
  • 任何二次曲线都可以完美地表示为三次曲线。如果您的二次方程有点 {p1, p2, p3},则三次等效(完全等效)使用 {p1, ((p1+2*p2)/3), ((2*p2+p3)/3), p3}。不要重新发明轮子 =)

标签: java math bezier


【解决方案1】:

作为替代答案,为了规避 Martin R 指出的问题,您可以简单地构建一个查找表并以这种方式将坐标解析为曲线上。在构建曲线时,为增量 t 值生成 N 点坐标数组,然后在需要测试坐标是否位于曲线上时,找到最近的 t em> 通过检查该坐标是否在查找表中或“足够接近”查找表中的坐标来为该坐标赋值。在代码中:

point[] points = new point[100];
for(i=0; i<100; i++) {
  t = i/100;
  points[i] = new point(computeX(t), computeY(t));
}

然后当您需要进行曲线测试时:

for(i=0; i<points.length; i++) {
  point = points[i];
  if(abs(point-coordinate)<3) {
    // close enough to the curve to count,
    // so we can use t value map(i,0,100,0,1)
  }
}

构建 LUT 几乎不需要花费任何成本,因为我们已经生成了这些坐标值以绘制曲线,并且您不会在没有首先确保坐标是偶数的情况下运行曲线测试在曲线的边界框中。

【讨论】:

    【解决方案2】:

    这里有错误:

    double[] ty = obtainTs(p.y, p0, p1, p2);
    

    因为obtainTs() 使用 p0、p1、p2 的 x 坐标来找到 p 的 y 坐标。

    如果将方法参数更改为int(可以是点的x坐标或y坐标):

    public static double[] obtainTs (int comp, int p0, int p1, int p2) {
        double a = p0 - 2*p1 + p2;
        double b = 2*p1 - 2*p0 ;
        double c = p0 - comp;
    
        double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
        double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
    
        return new double[] {t1, t2};
    }
    

    然后调用它

    double[] tx = obtainTs(p.x, p0.x, p1.x, p2.x);
    double[] ty = obtainTs(p.y, p0.y, p1.y, p2.y);
    

    那么您的测试代码将返回“true”(使用 ERROR = 0.02 进行测试)。


    注意,如果你写下方程式

    B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2
    

    对于 x 和 y 坐标,您可以消除 t^2-Term 并得到一个 t 的线性方程。这给出了以下方法,可能会稍微简单一些 并且不使用平方根:

    public static final boolean belongsQuadratic2 (Point p, Point p0, Point p1, Point p2) {
        double ax = p0.x - 2*p1.x + p2.x;
        double bx = 2*p1.x - 2*p0.x ;
        double cx = p0.x - p.x;
    
        double ay = p0.y - 2*p1.y + p2.y;
        double by = 2*p1.y - 2*p0.y ;
        double cy = p0.y - p.y;
    
        // "Candidate" for t:
        double t = -(cx*ay - cy*ax)/(bx*ay - by*ax);
        if (t < 0 || t > 1)
            return false;
        // Compute the point corresponding to this candidate value ...
        Point q = Bezier.quadratic(p0, p1, p2, t);
        // ... and check if it is near the given point p:
        return Math.abs(q.x - p.x) <= 1 && Math.abs(q.y - p.y) <= 1;
    }
    

    当然,必须检查特殊情况,例如bx*ay - by*ax == 0

    还要注意,如果一个点位于曲线上,则很难准确地确定,因为 点坐标四舍五入为整数。

    【讨论】:

    • 谢谢@Martin!我喜欢你没有平方根的方法,我会试一试:)
    猜你喜欢
    • 2015-07-12
    • 1970-01-01
    • 1970-01-01
    • 2017-12-12
    • 2011-08-03
    • 2011-03-10
    • 2016-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多